{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a461c72d",
   "metadata": {
    "id": "a461c72d"
   },
   "source": [
    "# OptiGuide Example\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59a05fc7",
   "metadata": {
    "id": "59a05fc7"
   },
   "source": [
    "Here we give a simple example, as designed and illustrated in the [OptiGuide paper](https://arxiv.org/abs/2307.03875).\n",
    "While the original paper is designed specifically for supply chain optimization, the general framework can be easily adapted to other applications with coding capacity.\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e92d200",
   "metadata": {
    "id": "5e92d200"
   },
   "source": [
    "## OptiGuide for Supply Chain Optimization: System Design Overview\n",
    "\n",
    "The original system design for OptiGuide, tailored for supply chain optimization, is presented below.\n",
    "\n",
    "The collaboration among three agents -- Coder, Safeguard, and Interpreter -- lies at the core of this system. They leverage a set of external tools and a large language model (LLM) to address users' questions related to supply chain applications. For a comprehensive understanding of the design and data flow, detailed information can be found in the original [paper](https://arxiv.org/abs/2307.03875).\n",
    "\n",
    "\n",
    "![optiguide system](https://www.beibinli.com/docs/optiguide/optiguide_system.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b7f90c8",
   "metadata": {
    "id": "8b7f90c8"
   },
   "source": [
    "## New Implementation\n",
    "\n",
    "\n",
    "\n",
    "![](new_design.png)\n",
    "\n",
    "Advantages of this multi-agent design with autogen:\n",
    "- Collaborative Problem Solving: The collaboration among the user proxy agent and the assistant agents fosters a cooperative problem-solving environment. The agents can share information and knowledge, allowing them to complement each other's abilities and collectively arrive at better solutions. On the other hand, the Safeguard acts as a virtual adversarial checker, which can perform another safety check pass on the generated code.\n",
    "\n",
    "- Modularity: The division of tasks into separate agents promotes modularity in the system. Each agent can be developed, tested, and maintained independently, simplifying the overall development process and facilitating code management.\n",
    "\n",
    "- Memory Management: The OptiGuide agent's role in maintaining memory related to user interactions is crucial. The memory retention allows the agents to have context about a user's prior questions, making the decision-making process more informed and context-aware.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ReAgLnDma3oI",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ReAgLnDma3oI",
    "outputId": "c95a23e8-ddb9-4fd2-f218-c40a51cbcbb0"
   },
   "outputs": [],
   "source": [
    "# Install Required Packages\n",
    "# %pip install optiguide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9a3b79c4",
   "metadata": {
    "id": "9a3b79c4"
   },
   "outputs": [],
   "source": [
    "# test Gurobi installation\n",
    "import gurobipy as gp\n",
    "from gurobipy import GRB\n",
    "from eventlet.timeout import Timeout\n",
    "\n",
    "# import auxillary packages\n",
    "import requests  # for loading the example source code\n",
    "import openai\n",
    "\n",
    "# import autogen\n",
    "import autogen\n",
    "from autogen.agentchat import Agent, UserProxyAgent\n",
    "from optiguide import OptiGuideAgent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "aedf19e7",
   "metadata": {
    "id": "aedf19e7"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:root:The specified config_list file 'OAI_CONFIG_LIST' does not exist.\n"
     ]
    }
   ],
   "source": [
    "config_list = autogen.config_list_from_json(\n",
    "    \"OAI_CONFIG_LIST\",\n",
    "    filter_dict={\n",
    "        \"model\": {\n",
    "            \"gpt-4\",\n",
    "            \"gpt4\",\n",
    "            \"gpt-4-32k\",\n",
    "            \"gpt-4-32k-0314\",\n",
    "            \"gpt-3.5-turbo\",\n",
    "            \"gpt-3.5-turbo-16k\",\n",
    "            \"gpt-3.5-turbo-0301\",\n",
    "            \"chatgpt-35-turbo-0301\",\n",
    "            \"gpt-35-turbo-v0301\",\n",
    "        }\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9e7e728",
   "metadata": {
    "id": "e9e7e728"
   },
   "source": [
    "Now, let's import the source code (loading from URL) and also some training examples (defined as string blow)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ca962ac5",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ca962ac5",
    "outputId": "4a789991-8ba1-46f3-aad5-7fbaaaff7f02"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "import time\n",
      "from gurobipy import GRB, Model\n",
      "\n",
      "# Example data\n",
      "\n",
      "capacity_in_supplier = {'supplier1': 150, 'supplier2': 50, 'supplier3': 100}\n",
      "\n",
      "shipping_cost_from_supplier_to_roastery = {\n",
      "    ('supplier1', 'roastery1'): 5,\n",
      "    ('supplier1', 'roastery2'): 4,\n",
      ".\n",
      ".\n",
      ".\n",
      "\n",
      "# Solve\n",
      "m.update()\n",
      "model.optimize()\n",
      "\n",
      "print(time.ctime())\n",
      "if m.status == GRB.OPTIMAL:\n",
      "    print(f'Optimal cost: {m.objVal}')\n",
      "else:\n",
      "    print(\"Not solved to optimality. Optimization status:\", m.status)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Get the source code of our coffee example\n",
    "code_url = \"https://raw.githubusercontent.com/microsoft/OptiGuide/main/benchmark/application/coffee.py\"\n",
    "response  = requests.get(code_url)\n",
    "\n",
    "\n",
    "# Check if the request was successful\n",
    "if response.status_code == 200:\n",
    "    # Get the text content from the response\n",
    "    code = response.text\n",
    "else:\n",
    "    raise RuntimeError(\"Failed to retrieve the file.\")\n",
    "# code = open(code_url, \"r\").read() # for local files\n",
    "\n",
    "\n",
    "# show the first head and tail of the source code\n",
    "print(\"\\n\".join(code.split(\"\\n\")[:10]))\n",
    "print(\".\\n\" * 3)\n",
    "print(\"\\n\".join(code.split(\"\\n\")[-10:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e31c4b36",
   "metadata": {
    "code_folding": [],
    "id": "e31c4b36"
   },
   "outputs": [],
   "source": [
    "# In-context learning examples.\n",
    "example_qa = \"\"\"\n",
    "----------\n",
    "Question: Why is it not recommended to use just one supplier for roastery 2?\n",
    "Answer Code:\n",
    "```python\n",
    "z = m.addVars(suppliers, vtype=GRB.BINARY, name=\"z\")\n",
    "m.addConstr(sum(z[s] for s in suppliers) <= 1, \"_\")\n",
    "for s in suppliers:\n",
    "    m.addConstr(x[s,'roastery2'] <= capacity_in_supplier[s] * z[s], \"_\")\n",
    "```\n",
    "\n",
    "----------\n",
    "Question: What if there's a 13% jump in the demand for light coffee at cafe1?\n",
    "Answer Code:\n",
    "```python\n",
    "light_coffee_needed_for_cafe[\"cafe1\"] = light_coffee_needed_for_cafe[\"cafe1\"] * (1 + 13/100)\n",
    "```\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a5a7d7e",
   "metadata": {
    "id": "5a5a7d7e"
   },
   "source": [
    "Now, let's create an OptiGuide agent and also a user.\n",
    "\n",
    "For the OptiGuide agent, we only allow \"debug_times\" to be 1, which means it can debug its answer once if it encountered errors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "af53727c",
   "metadata": {
    "id": "af53727c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[autogen.oai.client: 01-03 14:46:48] {80} WARNING - openai client was provided with an empty config_list, which may not be intended.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:autogen.oai.client:openai client was provided with an empty config_list, which may not be intended.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[autogen.oai.client: 01-03 14:46:48] {80} WARNING - openai client was provided with an empty config_list, which may not be intended.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:autogen.oai.client:openai client was provided with an empty config_list, which may not be intended.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[autogen.oai.client: 01-03 14:46:48] {80} WARNING - openai client was provided with an empty config_list, which may not be intended.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:autogen.oai.client:openai client was provided with an empty config_list, which may not be intended.\n"
     ]
    }
   ],
   "source": [
    "%%capture\n",
    "agent = OptiGuideAgent(\n",
    "    name=\"OptiGuide Coffee Example\",\n",
    "    source_code=code,\n",
    "    debug_times=1,\n",
    "    example_qa=\"\",\n",
    "    llm_config={\n",
    "        \"seed\": 42,\n",
    "        \"config_list\": config_list,\n",
    "    }\n",
    ")\n",
    "\n",
    "user = UserProxyAgent(\n",
    "    \"user\", max_consecutive_auto_reply=0,\n",
    "    human_input_mode=\"NEVER\", code_execution_config=False\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd615e87",
   "metadata": {
    "id": "bd615e87"
   },
   "source": [
    "Now, let's create a user's question."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "24a76f67",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "24a76f67",
    "outputId": "2399e32e-fe3f-429f-9c40-fcc75605741d",
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[33muser\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "What if we prohibit shipping from supplier 1 to roastery 2?\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to writer):\n",
      "\n",
      "\n",
      "Answer Code:\n",
      "\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mwriter\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "```python\n",
      "model.addConstr(x[('supplier1', 'roastery2')] == 0, \"no_shipping_supplier1_to_roastery2\")\n",
      "```\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to safeguard):\n",
      "\n",
      "\n",
      "--- Code ---\n",
      "model.addConstr(x[('supplier1', 'roastery2')] == 0, \"no_shipping_supplier1_to_roastery2\")\n",
      "\n",
      "--- One-Word Answer: SAFE or DANGER ---\n",
      "\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33msafeguard\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "SAFE\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (win64)\n",
      "Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
      "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n",
      "Model fingerprint: 0x393c2fb7\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 1e+00]\n",
      "  Objective range  [2e+00, 1e+01]\n",
      "  Bounds range     [0e+00, 0e+00]\n",
      "  RHS range        [2e+01, 2e+02]\n",
      "Found heuristic solution: objective 3490.0000000\n",
      "Presolve time: 0.00s\n",
      "Presolved: 11 rows, 18 columns, 36 nonzeros\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "Found heuristic solution: objective 3486.0000000\n",
      "\n",
      "Root relaxation: objective 2.470000e+03, 11 iterations, 0.00 seconds (0.00 work units)\n",
      "\n",
      "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
      " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
      "\n",
      "*    0     0               0    2470.0000000 2470.00000  0.00%     -    0s\n",
      "\n",
      "Explored 1 nodes (11 simplex iterations) in 0.03 seconds (0.00 work units)\n",
      "Thread count was 32 (of 32 available processors)\n",
      "\n",
      "Solution count 3: 2470 3486 3490 \n",
      "\n",
      "Optimal solution found (tolerance 1.00e-04)\n",
      "Best objective 2.470000000000e+03, best bound 2.470000000000e+03, gap 0.0000%\n",
      "Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (win64)\n",
      "Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
      "Optimize a model with 12 rows, 18 columns and 37 nonzeros\n",
      "Model fingerprint: 0x94aaf59a\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 1e+00]\n",
      "  Objective range  [2e+00, 1e+01]\n",
      "  Bounds range     [0e+00, 0e+00]\n",
      "  RHS range        [2e+01, 2e+02]\n",
      "\n",
      "MIP start from previous solve did not produce a new incumbent solution\n",
      "MIP start from previous solve violates constraint no_shipping_supplier1_to_roastery2 by 80.000000000\n",
      "\n",
      "Found heuristic solution: objective 3490.0000000\n",
      "Presolve removed 2 rows and 1 columns\n",
      "Presolve time: 0.00s\n",
      "Presolved: 10 rows, 17 columns, 33 nonzeros\n",
      "Variable types: 0 continuous, 17 integer (0 binary)\n",
      "Found heuristic solution: objective 3485.0000000\n",
      "\n",
      "Root relaxation: objective 2.760000e+03, 10 iterations, 0.00 seconds (0.00 work units)\n",
      "\n",
      "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
      " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
      "\n",
      "*    0     0               0    2760.0000000 2760.00000  0.00%     -    0s\n",
      "\n",
      "Explored 1 nodes (10 simplex iterations) in 0.02 seconds (0.00 work units)\n",
      "Thread count was 32 (of 32 available processors)\n",
      "\n",
      "Solution count 3: 2760 3485 3490 \n",
      "\n",
      "Optimal solution found (tolerance 1.00e-04)\n",
      "Best objective 2.760000000000e+03, best bound 2.760000000000e+03, gap 0.0000%\n",
      "Wed Jan  3 14:46:51 2024\n",
      "Optimal cost: 2760.0\n",
      "\u001b[33mOptimization problem solved. The objective value is: 2760.0\u001b[0m\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to writer):\n",
      "\n",
      "Here are the execution results: Optimization problem solved. The objective value is: 2760.0\n",
      "\n",
      "Can you organize these information to a human readable answer?\n",
      "Remember to compare the new results to the original results you obtained in the\n",
      "beginning.\n",
      "\n",
      "--- HUMAN READABLE ANSWER ---\n",
      "\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mwriter\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "By prohibiting the shipping from supplier 1 to roastery 2, the total cost increased from 2470.0 to 2760.0. This indicates that shipping from supplier 1 to roastery 2 was part of the optimal solution and by preventing this path it resulted in a more costly distribution plan.\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to user):\n",
      "\n",
      "By prohibiting the shipping from supplier 1 to roastery 2, the total cost increased from 2470.0 to 2760.0. This indicates that shipping from supplier 1 to roastery 2 was part of the optimal solution and by preventing this path it resulted in a more costly distribution plan.\n",
      "\n",
      "--------------------------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "user.initiate_chat(agent, message=\"What if we prohibit shipping from supplier 1 to roastery 2?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "cbdd1f28",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "cbdd1f28",
    "outputId": "c5e943cf-fce7-4484-8cd8-433d4fede8e5",
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[33muser\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "What is the impact of supplier1 being able to supply only half the quantity at present?\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to writer):\n",
      "\n",
      "\n",
      "Answer Code:\n",
      "\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mwriter\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "```python\n",
      "# Reduce the capacity for supplier1\n",
      "capacity_in_supplier['supplier1'] = 75\n",
      "\n",
      "# Need to update the supply constraint for supplier1\n",
      "for constr in model.getConstrs():\n",
      "    if constr.constrName == \"supply_supplier1\":\n",
      "        model.remove(constr)\n",
      "        break\n",
      "\n",
      "model.addConstr(\n",
      "    sum(x[i] for i in shipping_cost_from_supplier_to_roastery.keys()\n",
      "        if i[0] == 'supplier1') <= capacity_in_supplier['supplier1'], \n",
      "    f\"supply_supplier1\")\n",
      "\n",
      "m.update()\n",
      "m.optimize()\n",
      "```\n",
      "This code will reduce the capacity of supplier1 from 150 to 75. It finds the constraint on supplier1's capacity in the model's constraints, then removes it and adds the new constraint with reduced capacity. Finally, it updates the model and solves it again to find the new optimal solution.\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to safeguard):\n",
      "\n",
      "\n",
      "--- Code ---\n",
      "# Reduce the capacity for supplier1\n",
      "capacity_in_supplier['supplier1'] = 75\n",
      "\n",
      "# Need to update the supply constraint for supplier1\n",
      "for constr in model.getConstrs():\n",
      "    if constr.constrName == \"supply_supplier1\":\n",
      "        model.remove(constr)\n",
      "        break\n",
      "\n",
      "model.addConstr(\n",
      "    sum(x[i] for i in shipping_cost_from_supplier_to_roastery.keys()\n",
      "        if i[0] == 'supplier1') <= capacity_in_supplier['supplier1'], \n",
      "    f\"supply_supplier1\")\n",
      "\n",
      "m.update()\n",
      "m.optimize()\n",
      "\n",
      "--- One-Word Answer: SAFE or DANGER ---\n",
      "\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33msafeguard\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "SAFE\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (win64)\n",
      "Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
      "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n",
      "Model fingerprint: 0x393c2fb7\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 1e+00]\n",
      "  Objective range  [2e+00, 1e+01]\n",
      "  Bounds range     [0e+00, 0e+00]\n",
      "  RHS range        [2e+01, 2e+02]\n",
      "Found heuristic solution: objective 3490.0000000\n",
      "Presolve time: 0.00s\n",
      "Presolved: 11 rows, 18 columns, 36 nonzeros\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "Found heuristic solution: objective 3486.0000000\n",
      "\n",
      "Root relaxation: objective 2.470000e+03, 11 iterations, 0.00 seconds (0.00 work units)\n",
      "\n",
      "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
      " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
      "\n",
      "*    0     0               0    2470.0000000 2470.00000  0.00%     -    0s\n",
      "\n",
      "Explored 1 nodes (11 simplex iterations) in 0.03 seconds (0.00 work units)\n",
      "Thread count was 32 (of 32 available processors)\n",
      "\n",
      "Solution count 3: 2470 3486 3490 \n",
      "\n",
      "Optimal solution found (tolerance 1.00e-04)\n",
      "Best objective 2.470000000000e+03, best bound 2.470000000000e+03, gap 0.0000%\n",
      "Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (win64)\n",
      "Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
      "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n",
      "Model fingerprint: 0x35381fc1\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 1e+00]\n",
      "  Objective range  [2e+00, 1e+01]\n",
      "  Bounds range     [0e+00, 0e+00]\n",
      "  RHS range        [2e+01, 1e+02]\n",
      "\n",
      "MIP start from previous solve did not produce a new incumbent solution\n",
      "MIP start from previous solve violates constraint supply_supplier1 by 5.000000000\n",
      "\n",
      "Presolve time: 0.00s\n",
      "Presolved: 11 rows, 18 columns, 36 nonzeros\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "\n",
      "Root relaxation: infeasible, 14 iterations, 0.00 seconds (0.00 work units)\n",
      "\n",
      "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
      " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
      "\n",
      "     0     0 infeasible    0               - infeasible      -     -    0s\n",
      "\n",
      "Explored 1 nodes (14 simplex iterations) in 0.02 seconds (0.00 work units)\n",
      "Thread count was 32 (of 32 available processors)\n",
      "\n",
      "Solution count 0\n",
      "\n",
      "Model is infeasible\n",
      "Best objective -, best bound -, gap -\n",
      "Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (win64)\n",
      "Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
      "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n",
      "Model fingerprint: 0x35381fc1\n",
      "Variable types: 0 continuous, 18 integer (0 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 1e+00]\n",
      "  Objective range  [2e+00, 1e+01]\n",
      "  Bounds range     [0e+00, 0e+00]\n",
      "  RHS range        [2e+01, 1e+02]\n",
      "Presolved: 11 rows, 18 columns, 36 nonzeros\n",
      "\n",
      "Continuing optimization...\n",
      "\n",
      "\n",
      "Explored 1 nodes (14 simplex iterations) in 0.01 seconds (0.00 work units)\n",
      "Thread count was 32 (of 32 available processors)\n",
      "\n",
      "Solution count 0\n",
      "\n",
      "Model is infeasible\n",
      "Best objective -, best bound -, gap -\n",
      "Wed Jan  3 14:47:02 2024\n",
      "Not solved to optimality. Optimization status: 3\n",
      "\n",
      "Computing Irreducible Inconsistent Subsystem (IIS)...\n",
      "\n",
      "           Constraints          |            Bounds           |  Runtime\n",
      "      Min       Max     Guess   |   Min       Max     Guess   |\n",
      "--------------------------------------------------------------------------\n",
      "        0        11         -         0        18         -           0s\n",
      "       11        11         -         0         0         -           0s\n",
      "\n",
      "IIS computed: 11 constraints, 0 bounds\n",
      "IIS runtime: 0.01 seconds (0.00 work units)\n",
      "\u001b[33minfeasible\n",
      "Conflicting Constraints:\n",
      "['flow_roastery2', 'flow_roastery1', 'supply_supplier3', 'supply_supplier2', 'light_demand_cafe1', 'dark_demand_cafe1', 'light_demand_cafe3', 'dark_demand_cafe3', 'light_demand_cafe2', 'dark_demand_cafe2', 'supply_supplier1']\u001b[0m\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to writer):\n",
      "\n",
      "Here are the execution results: infeasible\n",
      "Conflicting Constraints:\n",
      "['flow_roastery2', 'flow_roastery1', 'supply_supplier3', 'supply_supplier2', 'light_demand_cafe1', 'dark_demand_cafe1', 'light_demand_cafe3', 'dark_demand_cafe3', 'light_demand_cafe2', 'dark_demand_cafe2', 'supply_supplier1']\n",
      "\n",
      "Can you organize these information to a human readable answer?\n",
      "Remember to compare the new results to the original results you obtained in the\n",
      "beginning.\n",
      "\n",
      "--- HUMAN READABLE ANSWER ---\n",
      "\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mwriter\u001b[0m (to OptiGuide Coffee Example):\n",
      "\n",
      "Based on the new optimization results when the capacity of supplier1 is reduced to half (from 150 to 75), the problem becomes infeasible. This means there is no solution available to meet the coffee demand of each cafe considering the quantity that each supplier can provide.\n",
      "\n",
      "The conflicting constraints represent the points in the model where the coffee chain is unable to balance coffee supply with demand under the current conditions. In other words, the coffee chain cannot meet every cafe's coffee needs using the current suppliers' capabilities. The list of conflicting constraints includes supply constraints (quantities that each supplier can provide), conservation of flow constraints (balances between quantities supplied, roasted, and served to cafes), as well as specific coffee (light or dark) demand constraints at each cafe.\n",
      "\n",
      "Comparing these results to the previous optimization results indicates that supplier1 has a significant impact on the current supply chain operation. When its supplying capacity was higher, there existed an optimal solution with the minimum cost (2470). But when capacity of supplier1 was halved, it became impossible to meet the coffee demand under the current circumstances. This highlights the importance of maintaining supplier capabilities, as well as the need to explore alternatives when certain suppliers' capacities decrease significantly.\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "\u001b[33mOptiGuide Coffee Example\u001b[0m (to user):\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Based on the new optimization results when the capacity of supplier1 is reduced to half (from 150 to 75), the problem becomes infeasible. This means there is no solution available to meet the coffee demand of each cafe considering the quantity that each supplier can provide.\n",
      "\n",
      "The conflicting constraints represent the points in the model where the coffee chain is unable to balance coffee supply with demand under the current conditions. In other words, the coffee chain cannot meet every cafe's coffee needs using the current suppliers' capabilities. The list of conflicting constraints includes supply constraints (quantities that each supplier can provide), conservation of flow constraints (balances between quantities supplied, roasted, and served to cafes), as well as specific coffee (light or dark) demand constraints at each cafe.\n",
      "\n",
      "Comparing these results to the previous optimization results indicates that supplier1 has a significant impact on the current supply chain operation. When its supplying capacity was higher, there existed an optimal solution with the minimum cost (2470). But when capacity of supplier1 was halved, it became impossible to meet the coffee demand under the current circumstances. This highlights the importance of maintaining supplier capabilities, as well as the need to explore alternatives when certain suppliers' capacities decrease significantly.\n",
      "\n",
      "--------------------------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "user.initiate_chat(agent, message=\"What is the impact of supplier1 being able to supply only half the quantity at present?\")"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
